/*
 *  fx/Twister.cpp
 *
 *  Copyright 2009 Peter Barth
 *
 *  This file is part of Milkytracker.
 *
 *  Milkytracker is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Milkytracker is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "BasicTypes.h"
#include "Twister.h"
#include "Math3d.h"
#include "Texture.h"

#define PRGB2SHORT(r, g, b) (((unsigned short)((r)) << 11) + ((unsigned short)((g)) << 5) + (unsigned short)((b)))

static float phi = 0.0f;
static int heightFieldOffset = 0;

Twister::Twister (int width, int height, int center)
{
    int i;

    this->width = width;
    this->height = height;

    if (center == -1)
        this->center = height >> 1;
    else
        this->center = center;

    unsigned char* rgbTex = new unsigned char[256 * 256 * 3];
    unsigned char* rgbTex2 = new unsigned char[256 * 256 * 3];

    texture = new unsigned short[256 * 256];

    Texture::createSplineTexture(rgbTex);

    Texture::createPlasmaTexture(rgbTex2, 256, 5, 192, 140, 63);

    for (i = 0; i < 256 * 256; i++)
    {
        int r = (rgbTex2[i * 3] >> 0) - (rgbTex[i * 3] * 0) + 31 * 2;
        int g = (rgbTex2[i * 3 + 1] >> 0) - (rgbTex[i * 3 + 1] * 0) + 31 * 2;
        int b = (rgbTex2[i * 3 + 2] >> 0) - (rgbTex[i * 3 + 2] * 0) + 64 * 2;

        if (r < 0) r = 0;if (r > 255) r = 255;
        if (g < 0) g = 0;if (g > 255) g = 255;
        if (b < 0) b = 0;if (b > 255) b = 255;

        rgbTex[i * 3] = r;
        rgbTex[i * 3 + 1] = g;
        rgbTex[i * 3 + 2] = b;
    }

    Texture::convert24to16(texture, rgbTex, 256 * 256, 1);

    for (i = 0; i < 1024; i++)
    {
        float phi = (i / 512.0f) * (float)M_PI;
        sinTable[i] = (int)((float)sin(phi) * 65536.0f);
        cosTable[i] = (int)((float)cos(phi) * 65536.0f);
    }

    delete rgbTex;
    delete rgbTex2;

    zbuffer = new int[height];
}

Twister::~Twister ()
{
    delete zbuffer;

    delete texture;
}

void Twister::update(float syncFrac)
{
    phi += (syncFrac * 0.01f);

    heightFieldOffset += (int)(65536.0f * syncFrac);
}

void Twister::render(unsigned short* vscreen, unsigned int pitch)
{
    int XMAX = width;
    int YMAX = height;

    //unsigned int bgColor = RGB2SHORT(31*4,7*4,15*4);
    unsigned int bgColor = 0;

    unsigned int fill = (bgColor << 16) | bgColor;

    int twistStep = (int)((float)sin(phi * 4.0f) * 256.0f);

    for (int j = 0; j < YMAX; j++)
    {
        unsigned int* vPtr = (unsigned int*)(vscreen + j * pitch);
        for (int i = 0; i < XMAX >> 1; i++)
            *vPtr++ = fill;
    }

    int beta = (int)((sin(phi) / M_PI) * 512.0f * 256.0f);
    //int beta = 0;

    int theta = beta >> 6;

    int* zBuffer = zbuffer;

    int scale = (YMAX * 65536) / 200;

    int center = this->center;

    for (int scanline = 0; scanline < XMAX; scanline++)
    {

        //VectorFP sourcePoints[4] = {{-64*65536,0,64*65536},{64*65536,0,64*65536},{-64*65536,0,-64*65536},{64*65536,0,-64*65536}};

        for (int j = 0; j < NUMPOINTS; j++)
        {
            sourcePoints[j].u = (j * (256 / NUMPOINTS)) * 2;

            //int radius = 64+(heightmap[((scanline+(heightFieldOffset>>16))&255)*256+(sourcePoints[j].u&255)]>>2);

            //int radius = 64+(mySin((scanline<<11)+theta)>>11);

            int radius = 64;

            //sourcePoints[j].p.x = (int)(-sin(((float)j/(float)NUMPOINTS)*2*M_PI+beta)*radius*65536.0f);
            //sourcePoints[j].p.y = 0;
            //sourcePoints[j].p.z = (int)(-cos(((float)j/(float)NUMPOINTS)*2*M_PI+beta)*radius*65536.0f);

            sourcePoints[j].p.x = -mySin(((1024 * 256 / NUMPOINTS) * j + beta * 4)) * radius + (sinTable[theta & 1023] * 0);
            sourcePoints[j].p.y = 0;
            sourcePoints[j].p.z = -myCos(((1024 * 256 / NUMPOINTS) * j + beta * 4)) * radius;
        }

        theta += 4;

        beta += -(mySin(theta * 256) * twistStep) >> 15;

        sourcePoints[NUMPOINTS].p = sourcePoints[0].p;
        sourcePoints[NUMPOINTS].u = 256 * 2;

        int i;
        for (i = 0; i < YMAX; i++)
            zBuffer[i] = -0x7fffffff;

        for (i = 0; i < NUMPOINTS; i++)
        {
            MyVertex* p1;
            MyVertex* p2;

            p1 = &sourcePoints[i];
            p2 = &sourcePoints[i + 1];

            if (p2->p.x - p1->p.x > 0)
            {
                int x1, x2, z1, z2, u1, u2;
                x1 = (fpmul(p1->p.x, scale) >> 16) + center;
                x2 = (fpmul(p2->p.x, scale) >> 16) + center;
                z1 = (p1->p.z >> 14);
                z2 = (p2->p.z >> 14);
                u1 = (p1->u);
                u2 = (p2->u);

                int stepu = 0, stepz = 0;
                if (x2 != x1)
                {
                    stepu = (u2 - u1) * 65536 / (x2 - x1);
                    stepz = (z2 - z1) * 65536 / (x2 - x1);
                }
                int su = u1 * 65536, sz = z1 * 65536;

                if (x1 < 0)
                {
                    su += -x1 * stepu;
                    sz += -x1 * stepz;
                    x1 = 0;
                }
                if (x2 > YMAX)
                {
                    x2 = YMAX;
                }


                unsigned short* texline = texture + ((((heightFieldOffset >> 16) + scanline) << 1) & 255) * 256;
                unsigned short* vLine = (unsigned short*)vscreen + x1 * pitch + scanline;

                for (int px = x1; px < x2; px++)
                {
                    if (sz > zBuffer[px])
                    {
                        int INTENSITY = ((sz >> 16) + (32));

                        if (INTENSITY < 0) INTENSITY = 0;
                        if (INTENSITY > 256) INTENSITY = 256;

                        INTENSITY = (INTENSITY * INTENSITY) >> 7;

                        int TEXEL = texline[((su >> 16) & 255)];

                        /*int r=TEXEL;
                           r*=INTENSITY;
                           r>>=8;
                           r&=0xf800;
                           int g=TEXEL&0x7e0;
                           g*=INTENSITY;
                           g>>=8;
                           g&=0x7e0;
                           int b=TEXEL&0x1f;
                           b*=INTENSITY;
                           b>>=8;

                         *vLine = r+g+b;*/

                        int rgb16_1 = bgColor;
                        int rgb16_2 = TEXEL;
                        int b = (((rgb16_1 & 31) << 8) + INTENSITY * ((rgb16_2 & 31) - (rgb16_1 & 31))) >> 8;
                        int g = ((((rgb16_1 >> 5) & 63) << 8) + INTENSITY * (((rgb16_2 >> 5) & 63) - ((rgb16_1 >> 5) & 63))) >> 8;
                        int r = (((rgb16_1 >> 11) << 8) + INTENSITY * ((rgb16_2 >> 11) - (rgb16_1 >> 11))) >> 8;

                        if (r > 31) r = 31;
                        if (g > 63) g = 63;
                        if (b > 31) b = 31;

                        *vLine = PRGB2SHORT(r, g, b);

                        //*vLine = RGB2SHORT((INTENSITY*255)>>8,(INTENSITY*255)>>8,(INTENSITY*255)>>8);

                        //*vLine = TEXEL;


                        //buffptr[scanline*320+px] = texture[((heightFieldOffset+scanline)&255)*256+((su>>16)&255)];
                        zBuffer[px] = sz;
                        vLine += pitch;
                    }

                    /*int INTENSITY = sz>>16;

                       if (INTENSITY<0) INTENSITY = 0;
                       if (INTENSITY>255) INTENSITY = 255;

                       int TEXEL = texture[scanline*256+((su>>16)&255)];
                       //vir[vidink+x2] = tex[((((sv>>8)&0xff00)+((su>>16)&0xff)))];
                       int r=TEXEL;
                       r*=INTENSITY;
                       r>>=8;
                       r&=0xf800;
                       int g=TEXEL&0x7e0;
                       g*=INTENSITY;
                       g>>=8;
                       g&=0x7e0;
                       int b=TEXEL&0x1f;
                       b*=INTENSITY;
                       b>>=8;

                       buffptr[scanline*320+px] = r+g+b;*/

                    su += stepu;
                    sz += stepz;
                }


            }
        }


    }

}